/**
 * @file video
 * @author liaoxuezhi@baidu.com
 */
/* eslint fecs-indent: [0, "space", 2, 2] */

import * as React from 'react';
import {
  Component,
  PureComponent,
  PropTypes
} from 'react';
import {Renderer, filter, resolveVariable} from '../renderer';
import * as schema from './video-schema.json';
import { Player, Shortcut } from 'video-react';
import {padArr} from '../util';
import OnExpression from '../hoc/on-expression';
import {alert} from '../../widgets/alert/alert';
import * as cx from 'classnames';

// import css
import "video-react/dist/video-react.css";
import "./video.scss";

const str2seconds = str => str.split(':').reverse().reduce((seconds, value, index) => seconds + (parseInt(value, 10) || 0) * Math.pow(60, index), 0);

export class FlvSource extends Component {
  static propTypes = {
    src: PropTypes.string.isRequired,
    type: PropTypes.string,
    video: PropTypes.object,
  }

  constructor(props, context) {
    super(props, context);
  }

  componentDidMount() {
    let { src, video, config, manager, isLive, autoPlay, actions } = this.props;

    require(['flv.js'], flvjs => {
      // load hls video source base on hls.js
      if (flvjs.isSupported()) {
        video = video || manager.video && manager.video.video;

        let flvPlayer = flvjs.createPlayer({
          type: 'flv',
          url: src,
          isLive: isLive
        }, config);
        flvPlayer.attachMediaElement(video);
        this.flvPlayer = flvPlayer;
        let loaded = false;
        let timer;

        manager.subscribeToOperationStateChange((operation) => {
          const type = operation.operation.action;

          if (type === 'play') {
            clearTimeout(timer);
            if (!loaded) {
              loaded = true;
              flvPlayer.load();
            }

            flvPlayer.play();
          } else if (type === 'pause') {
            flvPlayer.pause();

            if (isLive) {
              timer = setTimeout(() => {
                actions.seek(0);
                flvPlayer.unload();
                loaded = false;
              }, 30000);
            }
          }
        });

        flvPlayer.on(flvjs.Events.RECOVERED_EARLY_EOF, () => {
          alert('直播已经结束');
        });
        flvPlayer.on(flvjs.Events.ERROR, () => {
          alert('视频加载失败');
        });

        if (autoPlay) {
          setTimeout(() => actions.play(), 200);
        }
      }
    });
  }

  componentWillUnmount() {
    if (this.flvPlayer) {
      this.flvPlayer.unload();
      this.flvPlayer.detachMediaElement();
    }
  }

  render() {
    return (
      <source
        src={this.props.src}
        type={this.props.type || 'video/x-flv'}
      />
    );
  }
}

export class HlsSource extends Component {
  static propTypes = {
    src: PropTypes.string.isRequired,
    type: PropTypes.string,
    video: PropTypes.object,
  }

  constructor(props, context) {
    super(props, context);
  }

  componentDidMount() {
    let { src, video, config, manager, isLive, autoPlay, actions } = this.props;

    require(['hls.js'], Hls => {
      // load hls video source base on hls.js
      if (Hls.isSupported()) {
        video = video || manager.video && manager.video.video;

        let hls = this.hls = new Hls({
          autoStartLoad: false
        });
        hls.attachMedia(video);
        hls.loadSource(src);

        let loaded = false;

        manager.subscribeToOperationStateChange((operation) => {
          const type = operation.operation.action;

          if (type === 'play') {
            if (!loaded) {
              loaded = true;
              hls.startLoad();
            }

            video.play();
          } else if (type === 'pause') {
            video.pause();
            hls.stopLoad();
            loaded = false;
          }
        });

        autoPlay && setTimeout(actions.play, 200);
      }
    });
  }

  componentWillUnmount() {
    if (this.hls) {
      this.hls.stopLoad();
      this.hls.detachMedia();
    }
  }

  render() {
    return (
      <source
        src={this.props.src}
        type={this.props.type || 'application/x-mpegURL'}
      />
    );
  }
}


@Renderer({
  name: 'video',
  desc: '...',
  schema,
  test: (path, config) => /(?:^|\/)video$/.test(path)
  || /(?:^|\/)form-item$/.test(path) && config.type === 'video'
})
@OnExpression
export default class VideoRenderer extends Component {
  static propTypes = {
    className: PropTypes.string
  };

  static defaultProps = {
    columnsCount: 8,
    isLive: false,
    jumpFrame: true
  };

  constructor(props) {
    super(props);

    this.state = {
      posterInfo: null,
      videoState: {}
    };

    this.frameRef = this.frameRef.bind(this);
    this.cursorRef = this.cursorRef.bind(this);
    this.playerRef = this.playerRef.bind(this);
    this.onImageLoaded = this.onImageLoaded.bind(this);
    this.onClick = this.onClick.bind(this);
  }

  onImageLoaded(e) {
    let image = new Image();
    image.onload = () => {
      this.setState({
        posterInfo: {
          width: image.width,
          height: image.height
        }
      });
      image = image.onload = null;
    };
    image.src = e.target.getAttribute('src');
  }

  frameRef(dom) {
    this.frameDom = dom;
  }

  cursorRef(dom) {
    this.cursorDom = dom;
  }

  playerRef(player) {
    this.player = player;

    player && player.subscribeToStateChange(state => {
      this.setState({
        videoState: state
      });

      if (!this.frameDom || !this.times) {
        return;
      }

      let index = 0;
      const times = this.times;
      const len = times.length;
      while (index < len) {
        if (times[index-1] && state.currentTime <= (times[index + 1] - (times[index + 1] - times[index]) / 2)) {
          break;
        } else if (state.currentTime <= times[index]) {
          break;
        }

        index++;
      }

      if (this.currentIndex !== index) {
        this.moveCursorToIndex(index);
      }
    });
  }

  moveCursorToIndex(index) {
    if (!this.frameDom || !this.cursorDom) {
      return;
    }
    const items = this.frameDom.querySelectorAll('.video-frame');

    if (items && items.length && items[index]) {
      this.currentIndex = index;
      const item = items[index];
      const frameRect = this.frameDom.getBoundingClientRect();
      const rect = item.getBoundingClientRect();
      this.cursorDom.setAttribute("style", `width: ${rect.width - 4}px; height: ${rect.height - 4}px; left: ${rect.left + 2 - frameRect.left}px; top: ${rect.top + 2 - frameRect.top}px;`);
    }
  }

  jumpToIndex(index) {
    if (!this.times || !this.player || !this.props.jumpFrame) {
      return;
    }
    const times = this.times;
    const player = this.player;

    player.seek(times[index] - (times[index] - (times[index - 1] || 0)) / 2);
    player.play();
  }

  onClick(e) {
    // 避免把所在 form 给提交了。
    e.preventDefault();
  }

  renderFrames() {
    let {
      frames,
      framesClassName,
      columnsCount,
      data,
      jumpFrame
    } = this.props;

    if (typeof frames === 'string' && frames[0] === '$') {
      frames = resolveVariable(frames, data);
    }

    if (!frames) {
      return null;
    }

    const items = [];
    const times = this.times = [];
    Object.keys(frames).forEach(time => {
      if (!frames[time]) {
        return;
      }

      times.push(str2seconds(time));

      items.push({
        time: time,
        src: frames[time]
      });
    });

    if (!items.length) {
      return null;
    }

    return (
      <div className={cx('pos-rlt video-frame-list', framesClassName)} ref={this.frameRef}>
        {padArr(items, columnsCount).map((items, i) => {
          let restCount = columnsCount - items.length;
          let blankArray = [];

          while (restCount--) {
            blankArray.push('');
          }

          return (
            <div className="pull-in-xxs" key={i}>
              <div className="hbox video-frame-item">
                {items.map((item, key) => (
                  <div className="col wrapper-xxs video-frame" key={key} onClick={() => this.jumpToIndex(i * columnsCount + key)}>
                    <img className="w-full" alt="poster" src={item.src} />
                    <div className="text-center">{item.time}</div>
                  </div>
                ))}

                {/* 补充空白 */restCount ? blankArray.map((_, index) => (
                    <div className="col wrapper-xxs" key={`blank_${index}`} />
                  )) : null}
              </div>
            </div>
          );
        })}
        {jumpFrame ? <span ref={this.cursorRef} className="video-cursor" /> : null}
      </div>
    );
  }

  renderPlayer() {
    let {
      poster,
      src,
      autoPlay,
      muted,
      name,
      data,
      amisConfig,
      locals,
      isLive,
      minVideoDuration,
      videoType,
      playerClassName
    } = this.props;

    let source = src || name && data && data[name] || amisConfig && amisConfig.value;
    const videoState = this.state.videoState;
    let highlight = videoState.duration && minVideoDuration && videoState.duration < minVideoDuration;
    let src = filter(source, data, locals);
    let sourceNode;

    if (src && /\.flv(?:$|\?)/.test(src) && isLive || videoType === 'video/x-flv') {
      sourceNode = <FlvSource autoPlay={autoPlay} order={999.0} isLive={isLive} src={src} />;
    } else if (src && /\.m3u8(?:$|\?)/.test(src) || videoType === 'application/x-mpegURL') {
      sourceNode = <HlsSource autoPlay={autoPlay} order={999.0} src={src} />;
    } else {
      sourceNode = <source src={src} />;
    }

    return (
      <div className={playerClassName}>
        <Player
          ref={this.playerRef}
          poster={filter(poster, data, locals)}
          src={filter(source, data, locals)}
          autoPlay={autoPlay}
          muted={muted}
        >
          {sourceNode}
          <Shortcut disabled />
        </Player>

        {highlight ? (<p className="m-t-xs text-danger">视频时长小于 {minVideoDuration} 秒</p>) : null}
      </div>
    );
  }

  renderPosterAndPlayer() {
    let {
      poster,
      data,
      locals,
      minPosterDimension
    } = this.props;
    const posterInfo = this.state.posterInfo || {};
    let dimensionClassName = '';

    if (posterInfo && minPosterDimension
      && (minPosterDimension.width || minPosterDimension.height)
      && (minPosterDimension.width > posterInfo.width || minPosterDimension.height > posterInfo.height)
    ) {
      dimensionClassName = 'text-danger';
    }

    return (
      <div className="pull-in-xs">
        <div className="hbox">
          <div className="col">
            <div className="wrapper-xs">
              <img onLoad={this.onImageLoaded} className="w-full" alt="poster" src={filter(poster, data, locals)} />
              <p className="m-t-xs">封面 <span className={dimensionClassName}>{posterInfo.width || '-'} x {posterInfo.height || '-'}</span>
                {dimensionClassName ? (<span> 封面尺寸小于 <span className="text-danger">{minPosterDimension.width || '-'} x {minPosterDimension.height || '-'}</span></span>) : null}
              </p>
            </div>
          </div>
          <div className="col"><div className="wrapper-xs">{this.renderPlayer()}</div></div>
        </div>
      </div>
    );
  }

  render() {
    let {
      splitPoster,
      className
    } = this.props;

    return (
      <div className={className} onClick={this.onClick}>
        {this.renderFrames()}
        {splitPoster ? this.renderPosterAndPlayer() : this.renderPlayer()}
      </div>
    );
  }
}
